package com.tinkerapps.protocols.ssdp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.SocketException;
import java.net.UnknownHostException;

/**
 * Socket for SSDP communication
 */
public class SSDPSocket {

	MulticastSocket socket;
	boolean closing = false;
	private static InetAddress ssdpGroup; 

	/**
	 * Initialize SSDP multicast group address
	 */
	static
	{
		ensureMulticastGroupInitiallized();
	}
	
	/**
	 * Try initializing multicast group address if not already initialized
	 * @return 
	 */
	private static synchronized void ensureMulticastGroupInitiallized() {
		if (ssdpGroup == null) {
			try {
				ssdpGroup = InetAddress.getByName(Constants.IPv4);
			} catch (UnknownHostException e) {
				e.printStackTrace();
				ssdpGroup = null;
			}
		}
	}
	
	/**
	 * Open the socket
	 * @throws IOException 
	 */
	public void open() throws IOException {
		if (socket != null)
			close();
		ensureMulticastGroupInitiallized();
		if (ssdpGroup == null)
			throw new IllegalStateException("SSDP multicast group not initialized");
		socket = new MulticastSocket(Constants.PORT);
		socket.joinGroup(ssdpGroup);
		closing = false;
	}
	
	/**
	 * Close the socket
	 * @throws IOException 
	 */
	public void close() throws IOException {
		if (socket == null)
			throw new IllegalStateException("Socket is not open");
		ensureMulticastGroupInitiallized();
		if (ssdpGroup == null)
			throw new IllegalStateException("SSDP multicast group not initialized");
		socket.leaveGroup(ssdpGroup);
		closing = true;
		socket.close();
		socket = null;
	}
	
	/**
	 * Send data
	 * @param data Data to send
	 * @throws IOException
	 */
	public void send(byte[] data) throws IOException {
		if (data == null)
			throw new IllegalArgumentException("argument 'data' is null");
		if (socket == null)
			throw new IllegalStateException("Socket is not open");
		if (data.length > 0)
			socket.send(new DatagramPacket(data, data.length, ssdpGroup, Constants.PORT));
	}
	
	/**
	 * Receive data
	 * @return received data; null if socket closed
	 * @throws IOException 
	 */
	public DatagramPacket receive(byte[] buffer) throws IOException {
		if (buffer == null)
			throw new IllegalArgumentException("argument 'buffer' is null");
		if (socket == null)
			throw new IllegalStateException("Socket is not open");
		DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
		try {
			socket.receive(packet);
		} catch (IOException ex) {
			// if closing socket then don't re-throw
			if (closing && (ex instanceof SocketException))
				return null;
			throw ex;
		}
		return packet;
	}
}
